package com.koi.system.config.exceptions;

import com.koi.system.config.common.AjaxResult;
import com.koi.system.config.constants.HttpStatus;
import com.koi.system.config.common.ParamsException;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.jdbc.UncategorizedSQLException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.yaml.snakeyaml.constructor.DuplicateKeyException;

import javax.servlet.http.HttpServletRequest;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 全局异常处理
 * @author 大猫
 * @date 2021/12/9 13:18
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    /**
     * 全局异常，把这个全局异常注释掉才能够给使用@Validated校验
     * @author 大猫
     * @date 2021/12/7 15:44
     */
    @ExceptionHandler(value = Exception.class)
    public AjaxResult exceptionHandler(Exception e){
        //e.printStackTrace();
        log.error("系统全局异常-->",e);
        return AjaxResult.error(HttpStatus.ERROR,"系统未知异常，请稍后重试");
    }

    /**
     * 参数校验 AssertUtil校验类
     * @author 大猫
     * @date 2021/12/7 15:44
     */
    @ExceptionHandler(value = ParamsException.class)
    public AjaxResult paramsExceptionHandler(ParamsException e){
        //e.printStackTrace();
        log.error("参数校验异常-->",e);
        return AjaxResult.error(HttpStatus.MY_BAD_PARAMS, e.getMsg());
    }


    /**
     * 当系统出现MethodArgumentNotValidException这个异常时，会调用下面的方法
     * @param e
     * @return
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)//MethodArgumentNotValidException如果页面传参是json对象如果为空能触发
    public AjaxResult jsonErrorHandler(MethodArgumentNotValidException e){
        log.error("1.页面传参是json对象为空异常-->",e);
        return getAjaxResult(e.getBindingResult());
    }


    /**
     * 当系统出现MethodArgumentNotValidException这个异常时，会调用下面的方法 @Validated拦截此校验注解提示
     * @param e
     * @return
     */
    @ExceptionHandler(value = BindException.class)//MethodArgumentNotValidException如果页面传参是json对象如果为空能触发 @Validated拦截此校验注解提示
    public AjaxResult jsonErrorHandler(BindException e){
        log.error("2.页面传参是json对象为空异常-->",e);
        return getAjaxResult(e.getBindingResult());
    }

    /**
     * 抽象出来的方法 @Validated拦截此校验注解提示
     * @param bindingResult
     * @return
     */
    private AjaxResult getAjaxResult(BindingResult bindingResult) {
        log.error("全局异常抽象方法-->");
        List<Map<String, Object>> list = new ArrayList<>();
        List<ObjectError> allErrors = bindingResult.getAllErrors();
        for (ObjectError allError : allErrors) {
            Map<String, Object> map = new HashMap<>();
            map.put("defaultMessage", allError.getDefaultMessage());
            map.put("objectName", allError.getObjectName());
            //注意，这里面拿到具体的某一个属性
            FieldError fieldError = (FieldError) allError;
            map.put("field", fieldError.getField());
            list.add(map);
        }
        return AjaxResult.fail("后端数据校验异常", list);
    }


    /**
     * 处理空指针异常
     * @author 大猫
     * @date 2022/5/26 9:40
     * @param e
     * @return com.ruoyi.common.core.domain.AjaxResult
     */
    @ExceptionHandler(NullPointerException.class)
    public AjaxResult exceptionHandler(NullPointerException e,HttpServletRequest request){
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',空指针异常.", requestURI, e);
        return AjaxResult.error("空指针异常,请联系管理员核实");
    }

    /**
     * DB异常
     * @author 大猫
     * @date 2022/5/26 9:26
     * @param e
     * @return com.ruoyi.common.core.domain.AjaxResult
     */
    @ExceptionHandler(UncategorizedSQLException.class)
    public AjaxResult handleUncategorizedSQLException(UncategorizedSQLException e,HttpServletRequest request){
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',数据库异常.", requestURI, e);
        return AjaxResult.error("数据库异常,请联系管理员核实");
    }

    /**
     * DB主键冲突异常
     * @param e
     * @return
     */
    @ExceptionHandler(DuplicateKeyException.class)
    public AjaxResult handleDuplicateKeyException(DuplicateKeyException e,HttpServletRequest request){
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',DB主键冲突异常", requestURI, e);
        return AjaxResult.error("DB主键冲突异常,请联系管理员核实");
    }

    /**
     * 非法sql异常
     * @author 大猫
     * @date 2022/5/26 9:25
     * @param e
     * @return com.ruoyi.common.core.domain.AjaxResult
     */
    @ExceptionHandler(BadSqlGrammarException.class)
    public AjaxResult badSqlGrammarException(BadSqlGrammarException e,HttpServletRequest request){
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',BAD-SQL异常", requestURI, e);
        return AjaxResult.error("BAD-SQL,请联系管理员核实");
    }

    /**
     * Query异常
     * @author 大猫
     * @date 2022/5/26 9:29
     * @param e
     * @return com.ruoyi.common.core.domain.AjaxResult
     */
    @ExceptionHandler(MyBatisSystemException.class)
    public AjaxResult queryException(MyBatisSystemException e,HttpServletRequest request){
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',Query异常", requestURI, e);
        return AjaxResult.error("Query异常,请联系管理员核实");
    }

    /**
     * DML 操作异常
     * @author 大猫
     * @date 2022/5/26 9:29
     * @param e
     * @return com.ruoyi.common.core.domain.AjaxResult
     */
    @ExceptionHandler(DataIntegrityViolationException.class)
    public AjaxResult dataIntegrityViolationException(MyBatisSystemException e,HttpServletRequest request){
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',DML操作异常", requestURI, e);
        return AjaxResult.error("DML操作异常,请联系管理员核实");
    }

    /**
     * 运算异常
     * @author 大猫
     * @date 2022/5/26 9:30
     * @param e
     * @return com.ruoyi.common.core.domain.AjaxResult
     */
    @ExceptionHandler(ArithmeticException.class)
    public AjaxResult arithmeticExceptionHandler(ArithmeticException e,HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',运算异常", requestURI, e);
        return AjaxResult.error("运算异常,请联系管理员核实");
    }

    /**
     * 类型转换异常
     * @author 大猫
     * @date 2022/5/26 9:31
     * @param e
     * @return com.ruoyi.common.core.domain.AjaxResult
     */
    @ExceptionHandler(ClassCastException.class)
    public AjaxResult classCastExceptionHandler(ClassCastException e,HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',类型转换异常", requestURI, e);
        return AjaxResult.error("类型转换异常,请联系管理员核实");
    }


    /**
     * 数据下标越界异常
     * @author 大猫
     * @date 2022/5/26 9:31
     * @param e
     * @return com.ruoyi.common.core.domain.AjaxResult
     */
    @ExceptionHandler(IndexOutOfBoundsException.class)
    public AjaxResult indexOutOfBoundsExceptionHandler(IndexOutOfBoundsException e,HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',数据下标越界异常", requestURI, e);
        return AjaxResult.error("数据下标越界异常,请联系管理员核实");
    }

    /**
     * 文件未找到异常
     * @author 大猫
     * @date 2022/5/26 9:32
     * @param e
     * @return com.ruoyi.common.core.domain.AjaxResult
     */
    @ExceptionHandler(FileNotFoundException.class)
    public AjaxResult fileNotFoundExceptionHandler(FileNotFoundException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',文件未找到异常", requestURI, e);
        return AjaxResult.error("文件未找到异常,请联系管理员核实");
    }


    /**
     * IO异常
     * @author 大猫
     * @date 2022/5/26 9:31
     * @param e
     * @return ResultBody
     */
    @ExceptionHandler(IOException.class)
    public AjaxResult iOExceptionHandler(IOException e,HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',IO异常", requestURI, e);
        return AjaxResult.error("IO异常,请联系管理员核实");
    }


    /**
     * 参数类型不匹配
     * @author 大猫
     * @date 2022/5/26 9:33
     * @param e
     * @return com.ruoyi.common.core.domain.AjaxResult
     */
    @ExceptionHandler({MethodArgumentTypeMismatchException.class})
    public AjaxResult requestTypeMismatch(MethodArgumentTypeMismatchException e,HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',参数类型不匹配异常", requestURI, e);
        return AjaxResult.error("参数类型不匹配异常,请联系管理员核实");
    }
}
